﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data;
using System.Globalization;
using System.Data.Common;

namespace ResourceBlender_Express {
    /// <summary>
    /// A window to show unused resources.
    /// </summary>
    public partial class WinUnusedResources : Window {
        public WinUnusedResources() :
            this(new ObservableSortedDictionary<string, CultureInfo>(new Comparers.CultureInfoEnglshNameComparer(), new Comparers.StringEqualityComparer())) { }

        /// <summary>
        /// Initializes a new <see cref="WinUnusedResources"/> to display the specified languages.
        /// </summary>
        /// <param name="languages">The languages to show as RadioButtons for which unused resources can be shown.</param>
        public WinUnusedResources(ObservableSortedDictionary<string, CultureInfo> languages) {
            InitializeComponent();
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                if(languages.Count == 0) {
                    Database db = null;
                    try {
                        if(db == null || !db.Connected) {
                            db = new Database();
                            db.OpenConnection();
                        }
                        using(Database.Command cmd = db.CreateCommand()) {
                            cmd.CommandText = string.Format(@"SELECT DISTINCT language FROM {0}resources", db.TablePrefix);
                            DbDataReader reader = cmd.ExecuteReader();
                            while(reader.Read()) {
                                CultureInfo info = Utils.GetCultureInfo(reader[0].ToString());
                                if(info != null) {
                                    languages.Add(info.Name, info);
                                }
                            }
                        }
                    } catch(Exception ex) {
                        dwe.Result = ex.Message;
                    } finally {
                        if(db != null && db.Connected) {
                            try {
                                db.CloseConnection();
                            } catch { }
                        }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                lstUnusedResources.IsEnabled = true;
                lstLanguages.ItemsSource = languages;
                if(languages.Count > 0) {
                    lstLanguages.SelectedIndex = 0;
                    if(rwe.Result == null) {
                        NavigationCommands.Refresh.Execute(null, null);
                    } else {
                        DialogBox.ShowAlert(this, "Could not connect to database - " + rwe.Result.ToString(), "Database Error");
                    }
                } else {
                    DialogBox.ShowAlert(this, "No resources have been added", "Database Error");
                    Close();
                }
            };
            SetStatus("Loading available languages...", true);
            lstUnusedResources.IsEnabled = false;
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Determines whether the RefreshResources command can be executed
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteRefreshResources(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && lstLanguages.SelectedValue != null;
        }

        /// <summary>
        /// Shows unused resources for the selected language.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void RefreshResources(object sender, RoutedEventArgs e) {
            if(lstLanguages.SelectedValue == null) {
                return;
            }
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                Database db = null;
                try {
                    if(db == null || !db.Connected) {
                        db = new Database();
                        db.OpenConnection();
                    }
                    using(Database.Command cmd = db.CreateCommand()) {
                        cmd.CommandText = string.Format(@"SELECT t.resourceid, t.translation, (SELECT COUNT(r.language) FROM {0}resources AS r WHERE r.resourceid = t.resourceid) AS number
                                   FROM {0}resources AS t LEFT JOIN {0}elements AS e ON t.resourceid = e.resourceid WHERE language = @lang
                                   GROUP BY t.resourceid, t.translation HAVING COUNT(e.resourceid) = 0", db.TablePrefix);
                        cmd.AddParameterWithValue("@lang", dwe.Argument);
                        DataTable dt = new DataTable();
                        Database.DataAdapter adapter = new Database.DataAdapter(cmd);
                        adapter.Fill(dt);
                        dwe.Result = dt;
                    }
                } catch(Exception ex) {
                    dwe.Result = ex.Message;
                } finally {
                    if(db != null && db.Connected) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                lstUnusedResources.IsEnabled = true;
                DataTable dt = rwe.Result as DataTable;
                if(dt != null) {
                    int numUnused = dt.DefaultView.Count;
                    SetStatus(string.Format("{0} unused {1} for {2}", numUnused, numUnused == 1 ? "resource" : "resources", ((KeyValuePair<string, CultureInfo>)lstLanguages.SelectedItem).Value.EnglishName), false);
                    lstUnusedResources.DataContext = dt;
                } else {
                    DialogBox.ShowAlert(this, "Error: Could not connect to database - " + rwe.Result.ToString(), "Database Error");
                }
            };
            SetStatus("Loading unused resources...", true);
            lstUnusedResources.IsEnabled = false;
            worker.RunWorkerAsync(lstLanguages.SelectedValue);
        }

        /// <summary>
        /// Determines whether ththe DeleteResources command can be executed
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteDeleteResources(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && lstUnusedResources.SelectedItems.Count > 0;
        }

        /// <summary>
        /// Prompts to delete resources from the database.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PromptDeleteResources(object sender, RoutedEventArgs e) {
            if(lstLanguages.SelectedValue == null) {
                return;
            }
            List<string> selectedResources = new List<string>();
            foreach(DataRowView row in lstUnusedResources.SelectedItems) {
                selectedResources.Add(row[0].ToString());
            }
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                Database db = null;
                try {
                    if(db == null || !db.Connected) {
                        db = new Database();
                        db.OpenConnection();
                    }
                    using(Database.Command cmd = db.CreateCommand()) {
                        List<string> selectedItems = dwe.Argument as List<string>;
                        string inClause = db.GenerateInClause("resourceid", selectedItems, false);
                        cmd.CommandText = "SELECT COUNT(language) FROM " + db.TablePrefix + "resources WHERE " + inClause;
                        dwe.Result = cmd.ExecuteScalar();
                    }
                } catch(Exception ex) {
                    dwe.Result = ex;
                } finally {
                    if(db != null && db.Connected) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                btnDelete.IsEnabled = true;
                lstUnusedResources.IsEnabled = true;
                if(rwe.Result is Exception) {
                    DialogBox.ShowAlert(this, "Error deleting resources: " + rwe.Result.ToString(), "Database Error");
                } else {
                    if(DialogBox.Show(this, string.Format("Are you sure you want to delete {0} resources.\n\nAcross all languages, {1} translations will be deleted.", selectedResources.Count, rwe.Result), "Confirm Delete", DialogBox.DialogBoxType.YesNo, DialogBox.DialogBoxIcon.Question, DialogBox.DialogBoxButton.No) == DialogBox.DialogBoxButton.Yes) {
                        DeleteResources(selectedResources);
                    }
                }
            };
            SetStatus("Loading...", true);
            lstUnusedResources.IsEnabled = false;
            btnDelete.IsEnabled = false;
            worker.RunWorkerAsync(selectedResources);
        }

        /// <summary>
        /// Deletes resources from the database.
        /// </summary>
        /// <param name="resourceIds">The resource ids.</param>
        private void DeleteResources(List<string> resourceIds) {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (s, dwe) => {
                Database db = null;
                try {
                    if(db == null || !db.Connected) {
                        db = new Database();
                        db.OpenConnection();
                    }
                    using(Database.Command cmd = db.CreateCommand()) {
                        List<string> selectedItems = dwe.Argument as List<string>;
                        string inClause = db.GenerateInClause("resourceid", selectedItems, false);
                        cmd.CommandText = "DELETE FROM " + db.TablePrefix + "resources WHERE " + inClause;
                        cmd.ExecuteNonQuery();
                    }
                } catch(Exception ex) {
                    dwe.Result = ex;
                } finally {
                    if(db != null && db.Connected) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                SetStatus(null, false);
                btnDelete.IsEnabled = true;
                lstUnusedResources.IsEnabled = true;
                try {
                    int numUnused = (lstUnusedResources.DataContext as DataTable).DefaultView.Count;
                    SetStatus(string.Format("{0} unused {1} for {2}", numUnused, numUnused == 1 ? "resource" : "resources", ((KeyValuePair<string, CultureInfo>)lstLanguages.SelectedItem).Value.EnglishName), false);
                } catch { }
                NavigationCommands.Refresh.Execute(null, null);
            };
            SetStatus("Loading...", true);
            btnDelete.IsEnabled = false;
            lstUnusedResources.IsEnabled = false;
            worker.RunWorkerAsync(resourceIds);
        }

        /// <summary>
        /// Determines whether all resources can be selected.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteSelectAll(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = this.IsLoaded && lstUnusedResources.HasItems;
        }

        /// <summary>
        /// Selects all resources.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void SelectAllResources(object sender, ExecutedRoutedEventArgs e) {
            lstUnusedResources.SelectAll();
        }

        /// <summary>
        /// Shows the help file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            Utils.OpenHelp("Unused.Resources.html");
        }

        /// <summary>
        /// Sets the text on the status message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="showProgress"><c>true</c> if the progress bar should be shown, otherwise <c>false</c>.</param>
        private void SetStatus(string message, bool showProgress) {
            prgStatus.Visibility = showProgress ? Visibility.Visible : Visibility.Hidden;
            if(message == null) {
                lblStatus.Visibility = Visibility.Hidden;
            } else {
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Content = message;
            }
        }
    }
}
